﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
using Microsoft.Ccr.Core;
using XcoAppSpaces.Contracts.Communication;
using XcoAppSpaces.Contracts.Messaging;
using XcoAppSpaces.Contracts.Serialization;
using XcoAppSpaces.Contracts.Service;
using System.Threading;
using XcoAppSpaces.Contracts.Worker;
using XcoAppSpaces.Core.Communication;
using XcoAppSpaces.Core.Communication.WorkerResolve;
using XcoAppSpaces.Contracts.Transport;
using XcoAppSpaces.Contracts.Security;
using test.XcoAppSpaces.Core.Communication.Helpers;

namespace test.XcoAppSpaces.Core.Communication.Performance
{
	[TestFixture]
	public class PerformanceTest
	{
		private MockRepository mocks = null;
		private IXcoWorkerRegistry workerRegistryMock1 = null;
		private IXcoWorkerRegistry workerRegistryMock2 = null;
		private IXcoSecurityService securityServiceMock1 = null;
		private IXcoSecurityService securityServiceMock2 = null;
		private Dispatcher d = null;
		private DispatcherQueue dpq = null;
		private int port1 = 0;
		private int port2 = 0;
		private string spaceaddress1 = null;
		private string spaceaddress2 = null;

		private PortSet<int, string> testWorkerContract = null;
		private Type testWorkerType = null;
		private string testWorkerName = null;
		private string testWorkerId = null;
		//private string causality1 = null;
		private Port<Exception> exceptionPort1 = null;

		private MessageTransmitter conn1 = null;
		private MessageTransmitter conn2 = null;
		//private string receivedItem = null;
		//private Causality activeCausality = null;

		private string defaultCommServiceName = "default";

		//private ManualResetEvent waitEvent;
		private DateTime startedAt;

		[SetUp]
		public void SetUp()
		{
			d = new Dispatcher();
			dpq = new DispatcherQueue("dpq", d);
			port1 = 8001;
			port2 = 8002;
			spaceaddress1 = Environment.MachineName.ToLower() + ":" + port1;
			spaceaddress2 = Environment.MachineName.ToLower() + ":" + port2;
			testWorkerContract = new PortSet<int, string>();
			testWorkerType = testWorkerContract.GetType();
			testWorkerName = "testworker";
			testWorkerId = testWorkerType.FullName + "/" + testWorkerName;
			//causality1 = "c1";
			exceptionPort1 = new Port<Exception>();
			//receivedItem = null;
			//activeCausality = null;

			mocks = new MockRepository();
			workerRegistryMock1 = mocks.StrictMock<IXcoWorkerRegistry>();
			workerRegistryMock2 = mocks.StrictMock<IXcoWorkerRegistry>();
			securityServiceMock1 = mocks.StrictMock<IXcoSecurityService>();
			securityServiceMock2 = mocks.StrictMock<IXcoSecurityService>();

			//expectations on mocks when instantiating InternalConnectors
			workerRegistryMock1.Register<ResolveWorker>(null, null);
			LastCall.IgnoreArguments();
			workerRegistryMock2.Register<ResolveWorker>(null, null);
			LastCall.IgnoreArguments();
			//TODO expectations on security mocks

			mocks.ReplayAll();
			//instantiate and start InternalConnectors
			//Dictionary<string, IXcoTransportService> commService = new Dictionary<string, IXcoTransportService>();
			//commService[defaultCommServiceName] = new XcoCommunicationServiceMock(port1);
			conn1 = new MessageTransmitter(CreateServiceRegistryMock(dpq, workerRegistryMock1, new XcoCommunicationServiceMock(port1), defaultCommServiceName, new BinarySerializerMock(), securityServiceMock1), new CommunicationEvents());
			//commService = new Dictionary<string, IXcoTransportService>();
            //commService[defaultCommServiceName] = new XcoCommunicationServiceMock(port2);
			conn2 = new MessageTransmitter(CreateServiceRegistryMock(dpq, workerRegistryMock2, new XcoCommunicationServiceMock(port2), defaultCommServiceName, new BinarySerializerMock(), securityServiceMock2), new CommunicationEvents());
			conn1.Start();
			conn2.Start();
			mocks.BackToRecordAll();
		}

		private IXcoServiceRegistry CreateServiceRegistryMock(DispatcherQueue dpq, IXcoWorkerRegistry localWorkers, IXcoTransportService commService, string defaultCommServiceName, IXcoSerializer serializer, IXcoSecurityService securityService)
		{
			ServiceRegistryMock mock = new ServiceRegistryMock();
			mock.Register(new CcrDispatcherServiceMock(dpq));
			mock.Register(localWorkers);
			mock.Register(new XcoServiceConfiguration
			              	{
			              		Service = commService,
			              		ServiceType = typeof (IXcoTransportService),
			              		Name = defaultCommServiceName,
			              		IsDefault = true
			              	});
			mock.Register(serializer);
			mock.Register(securityService);
			return mock;
		}

		[TearDown]
		public void TearDown()
		{
			try
			{
				conn1.Dispose();
				conn2.Dispose();
			}
			catch (Exception)
			{
			}
			mocks.VerifyAll();
			d.Dispose();
		}


		[Test]
		public void TestWorkerPerformance()
		{
			//expectations on mocks
			workerRegistryMock2.Exists(testWorkerId, true, false);
			LastCall.Return<bool>(true).Repeat.Any();
			workerRegistryMock2.Resolve(testWorkerId, true);
			LastCall.Return<IPort>(testWorkerContract).Repeat.Any();
			securityServiceMock2.CheckPermission(null, null, null, Guid.Empty);
			LastCall.IgnoreArguments().Repeat.Any();

			//run test
			mocks.ReplayAll();

			Arbiter.Activate(dpq, Arbiter.Receive(true, testWorkerContract.P0, ProcessIntMsg));
			//waitEvent = new ManualResetEvent(false);
			startedAt = DateTime.Now;

			//send message to remote worker
			for (int i = 0; i <= 5000; i++)
			{
				conn1.SendMessage(new MessageToSend(spaceaddress2, null, null, new WorkerMessage(testWorkerId, Guid.Empty, i), null, null));
			}
			Console.WriteLine("Send Finished: " + DateTime.Now.Subtract(startedAt).TotalSeconds);
			Thread.Sleep(1000); //wait for message to be posted
		}

		[Test]
		public void TestWorkerPerformanceWithCausalities()
		{
			//expectations on mocks
			workerRegistryMock2.Exists(testWorkerId, true, false);
			LastCall.Return<bool>(true).Repeat.Any();
			workerRegistryMock2.Resolve(testWorkerId, true);
			LastCall.Return<IPort>(testWorkerContract).Repeat.Any();
			securityServiceMock2.CheckPermission(null, null, null, Guid.Empty);
			LastCall.IgnoreArguments().Repeat.Any();

			//run test
			mocks.ReplayAll();

			Arbiter.Activate(dpq, Arbiter.Receive(true, testWorkerContract.P0, ProcessIntMsg));
			//waitEvent = new ManualResetEvent(false);
			startedAt = DateTime.Now;

			//send message to remote worker
			CausalityContext context = new CausalityContext(new[] {new CausalityInfo(new Causality("c", exceptionPort1))});
			for (int i = 0; i <= 5000; i++)
			{
				conn1.SendMessage(new MessageToSend(spaceaddress2, null, context, new WorkerMessage(testWorkerId, Guid.Empty, i), null, null));
			}
			Console.WriteLine("Send Finished: " + DateTime.Now.Subtract(startedAt).TotalSeconds);
			Thread.Sleep(1000); //wait for message to be posted
		}

		private void ProcessIntMsg(int msg)
		{
			if (msg % 1000 == 0)
				Console.WriteLine("{0} @ {1}", msg, DateTime.Now.Subtract(startedAt).TotalSeconds);
		}
	}
}
